package cServer

import (
	"reflect"
	"slices"
	"sync"

	assetfs "github.com/elazarl/go-bindata-assetfs"
	"github.com/gin-gonic/gin"

	"gitee.com/csingo/cController"
	"gitee.com/csingo/cLog"
	"gitee.com/csingo/cMiddleware"
)

type RouteItem struct {
	Type    RouteType       `json:"type"`
	Path    string          `json:"path"`
	Target  string          `json:"target"`
	Method  []string        `json:"method"`
	Handler gin.HandlerFunc `json:"-"`
	// Prefix          []gin.HandlerFunc `json:"-"`
	// Suffix          []gin.HandlerFunc `json:"-"`
	Middlewares     []gin.HandlerFunc `json:"-"`
	MiddlewareNames []string          `json:"middlewares"`

	// PrefixMiddlewares []string `json:"prefix_middlewares"`
	// SuffixMiddlewares []string `json:"suffix_middlewares"`
}

type ServerContainer struct {
	lock      sync.RWMutex
	instances map[string]any
	routes    []*RouteItem
	views     map[string]*assetfs.AssetFS
}

func (i *ServerContainer) Save(instance any) bool {
	i.lock.Lock()
	defer i.lock.Unlock()

	typ := reflect.TypeOf(instance)

	name := typ.Elem().Name()
	pkg := typ.Elem().PkgPath()

	index := pkg + "/" + name
	i.instances[index] = instance

	return true
}

func (i *ServerContainer) Get(name string) any {
	i.lock.RLock()
	defer i.lock.RUnlock()

	return i.instances[name]
}

func (i *ServerContainer) Remove(name string) bool {
	i.lock.Lock()
	defer i.lock.Unlock()

	delete(i.instances, name)

	return true
}

func (i *ServerContainer) Is(instance any) bool {
	return reflect.TypeOf(instance).Kind() == reflect.Ptr
}

func (i *ServerContainer) Range(f func(instance any)) {
	i.lock.RLock()
	defer i.lock.RUnlock()

	for _, item := range i.instances {
		f(item)
	}
}

func (i *ServerContainer) IsView(instance any) bool {
	var ok bool
	_, ok = instance.(*assetfs.AssetFS)
	return ok
}

func (i *ServerContainer) SaveView(instance *assetfs.AssetFS) bool {
	i.lock.Lock()
	defer i.lock.Unlock()

	if instance == nil {
		return false
	}

	i.views[instance.Prefix] = instance

	return true
}

func (i *ServerContainer) GetView(prefix string) *assetfs.AssetFS {
	i.lock.RLock()
	defer i.lock.RUnlock()

	return i.views[prefix]
}

func (i *ServerContainer) DI() {
	i.lock.Lock()
	defer i.lock.Unlock()

	for _, source := range i.instances {
		for _, target := range i.instances {
			if reflect.ValueOf(source).Kind() != reflect.Ptr || reflect.ValueOf(target).Kind() != reflect.Ptr {
				continue
			}

			objectVal := reflect.ValueOf(source).Elem()
			objectTyp := objectVal.Type()
			attrVal := reflect.ValueOf(target).Elem()
			attrType := attrVal.Type()
			for index := 0; index < objectTyp.NumField(); index++ {
				field := objectTyp.Field(index)
				if field.Type.Kind() != reflect.Ptr {
					continue
				}
				if !objectVal.Field(index).IsNil() {
					continue
				}
				if field.Type.Elem().Name() != attrType.Name() || field.Type.Elem().PkgPath() != attrType.PkgPath() {
					continue
				}
				if !field.IsExported() {
					continue
				}
				objectVal.Field(index).Set(reflect.ValueOf(target))
			}
		}
	}
}

func (i *ServerContainer) ParseRoute(route *RouteConf, middlewares []gin.HandlerFunc, middlewareNames []string, uri string) {
	var middleware gin.HandlerFunc
	var err error

	if route == nil {
		return
	}

	if route.Middlewares != nil {
		// 获取前置中间件
		for _, item := range route.Middlewares {
			middleware, err = cMiddleware.GetGinHandlerFunc(item.App, item.Name, item.Params)
			if err != nil {
				cLog.WithContext(nil, map[string]any{
					"source":     "cServer.ServerContainer.ParseRoute",
					"middleware": item,
					"err":        err.Error(),
				}, []cLog.Option{
					cLog.IncludePaths{},
				}).Error("获取 prefix middleware 异常")
				continue
			}
			middlewares = append(middlewares, middleware)
			middlewareNames = append(middlewareNames, item.App+"."+item.Name)
		}
	}

	if len(route.Routes) > 0 {
		for _, v := range route.Routes {
			i.ParseRoute(&v, middlewares, middlewareNames, uri+route.Path)
		}
	} else {
		item := &RouteItem{
			Type:            route.Type,
			Path:            uri + route.Path,
			Target:          route.Target,
			Method:          route.Method,
			Middlewares:     slices.Clone(middlewares),
			MiddlewareNames: slices.Clone(middlewareNames),
		}
		switch item.Type {
		default:
			fallthrough
		case RouteTypeApi:
			item.Handler, err = cController.GetGinHandlerFunc(route.App, route.Controller, route.Function, route.Handler)
			if err != nil {
				cLog.WithContext(nil, map[string]any{
					"source": "cServer.ServerContainer.ParseRoute",
					"route":  route,
					"err":    err.Error(),
				}, []cLog.Option{
					cLog.IncludePaths{},
				}).Error("获取 controller 异常")
				return
			}
		case RouteTypeGrpc:
			item.Path = route.Path
		case RouteTypeFile:
		case RouteTypeDir:
		case RouteTypeView:
		case RouteTypeGroup:
			return
		}
		i.routes = append(i.routes, item)
	}
}

func (i *ServerContainer) Routes() []*RouteItem {
	return i.routes
}

var container = &ServerContainer{
	lock:      sync.RWMutex{},
	instances: make(map[string]any),
	routes:    make([]*RouteItem, 0),
	views:     make(map[string]*assetfs.AssetFS),
}
